home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / pine / imap-3.0 / ANSI / c-client / nntpcdos.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-07-25  |  45.4 KB  |  1,537 lines

  1. /*
  2.  * Program:    Network News Transfer Protocol (NNTP) client routines for DOS
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    25 January 1993
  13.  * Last Edited:    26 July 1993
  14.  *
  15.  * Copyright 1993 by the University of Washington.
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include <ctype.h>
  38. #include <stdio.h>
  39. #include <errno.h>
  40. #include <fcntl.h>
  41. #include "mail.h"
  42. #include "osdep.h"
  43. #include <time.h>
  44. #include <sys\stat.h>
  45. #include "dawz.h"
  46. #undef LOCAL
  47. #include "smtp.h"
  48. #include "nntp.h"
  49. #include "nntpcdos.h"
  50. #include "rfc822.h"
  51. #include "misc.h"
  52.  
  53. /* NNTP mail routines */
  54.  
  55.  
  56. /* Driver dispatch used by MAIL */
  57.  
  58. DRIVER nntpdriver = {
  59.   "nntp",            /* driver name */
  60.   (DRIVER *) NIL,        /* next driver */
  61.   nntp_valid,            /* mailbox is valid for us */
  62.   nntp_parameters,        /* manipulate parameters */
  63.   nntp_find,            /* find mailboxes */
  64.   nntp_find_bboards,        /* find bboards */
  65.   nntp_find_all,        /* find all mailboxes */
  66.   nntp_find_all_bboards,    /* find all bboards */
  67.   nntp_subscribe,        /* subscribe to mailbox */
  68.   nntp_unsubscribe,        /* unsubscribe from mailbox */
  69.   nntp_subscribe_bboard,    /* subscribe to bboard */
  70.   nntp_unsubscribe_bboard,    /* unsubscribe from bboard */
  71.   nntp_create,            /* create mailbox */
  72.   nntp_delete,            /* delete mailbox */
  73.   nntp_rename,            /* rename mailbox */
  74.   nntp_mopen,            /* open mailbox */
  75.   nntp_close,            /* close mailbox */
  76.   nntp_fetchfast,        /* fetch message "fast" attributes */
  77.   nntp_fetchflags,        /* fetch message flags */
  78.   nntp_fetchstructure,        /* fetch message envelopes */
  79.   nntp_fetchheader,        /* fetch message header only */
  80.   nntp_fetchtext,        /* fetch message body only */
  81.   nntp_fetchbody,        /* fetch message body section */
  82.   nntp_setflag,            /* set message flag */
  83.   nntp_clearflag,        /* clear message flag */
  84.   nntp_search,            /* search for message based on criteria */
  85.   nntp_ping,            /* ping mailbox to see if still alive */
  86.   nntp_check,            /* check for new messages */
  87.   nntp_expunge,            /* expunge deleted messages */
  88.   nntp_copy,            /* copy messages to another mailbox */
  89.   nntp_move,            /* move messages to another mailbox */
  90.   nntp_append,            /* append string message to mailbox */
  91.   nntp_gc            /* garbage collect stream */
  92. };
  93.  
  94.                 /* prototype stream */
  95. MAILSTREAM nntpproto = {&nntpdriver};
  96.  
  97. /* NNTP mail validate mailbox
  98.  * Accepts: mailbox name
  99.  * Returns: our driver if name is valid, NIL otherwise
  100.  */
  101.  
  102. DRIVER *nntp_valid (char *name)
  103. {
  104.                 /* must be bboard */
  105.   return *name == '*' ? mail_valid_net (name,&nntpdriver,NIL,NIL) : NIL;
  106. }
  107.  
  108.  
  109. /* News manipulate driver parameters
  110.  * Accepts: function code
  111.  *        function-dependent value
  112.  * Returns: function-dependent return value
  113.  */
  114.  
  115. void *nntp_parameters (long function,void *value)
  116. {
  117.   fatal ("Invalid nntp_parameters function");
  118.   return NIL;
  119. }
  120.  
  121. /* NNTP mail find list of mailboxes
  122.  * Accepts: mail stream
  123.  *        pattern to search
  124.  */
  125.  
  126. void nntp_find (MAILSTREAM *stream,char *pat)
  127. {
  128.   /* Always a no-op */
  129. }
  130.  
  131.  
  132. /* NNTP mail find list of bboards
  133.  * Accepts: mail stream
  134.  *        pattern to search
  135.  */
  136.  
  137. void nntp_find_bboards (MAILSTREAM *stream,char *pat)
  138. {
  139.   FILE *f = NIL;
  140.   void *s;
  141.   char *t,*u,*bbd,*patx,tmp[MAILTMPLEN],patc[MAILTMPLEN];
  142.   if (stream) {            /* use .newsrc if a stream given */
  143.                 /* begin with a host specification? */
  144.     if (((*pat == '{') || ((*pat == '*') && (pat[1] == '{'))) &&
  145.     (t = strchr (pat,'}')) && *(patx = ++t)) {
  146.       if (*pat == '*') pat++;    /* yes, skip leading * (old Pine behavior) */
  147.       strcpy (tmp,pat);        /* copy host name */
  148.       bbd = tmp + (patx - pat);    /* where we write the bboards */
  149.     }
  150.     else {            /* no host specification */
  151.       bbd = tmp;        /* no prefix */
  152.       patx = pat;        /* use entire specification */
  153.     }
  154.                 /* check all newsgroups from .newsrc */
  155.     while (t = nntp_read_sdb (&f)) {
  156.       if (u = strchr (t,':')) {    /* subscribed newsgroup? */
  157.     *u = '\0';        /* tie off at end of name */
  158.     if (pmatch (t,patx)) {    /* pattern patch */
  159.       strcpy (bbd,t);    /* write newsgroup name after hostspec */
  160.       mm_bboard (tmp);
  161.     }
  162.       }
  163.       fs_give ((void **) &t);    /* discard the line */
  164.     }
  165.   }
  166.                 /* use subscription manager if no stream */
  167.   else while (t = sm_read (&s))
  168.     if ((*t == '*') && pmatch (t+1,pat)) mm_bboard (t+1);
  169. }
  170.  
  171. /* NNTP mail find list of all mailboxes
  172.  * Accepts: mail stream
  173.  *        pattern to search
  174.  */
  175.  
  176. void nntp_find_all (MAILSTREAM *stream,char *pat)
  177. {
  178.   /* Always a no-op */
  179. }
  180.  
  181.  
  182. /* NNTP mail find list of all bboards
  183.  * Accepts: mail stream
  184.  *        pattern to search
  185.  */
  186.  
  187. void nntp_find_all_bboards (MAILSTREAM *stream,char *pat)
  188. {
  189.   char *s,*t,*bbd,*patx,tmp[MAILTMPLEN];
  190.                 /* use .newsrc if a stream given */
  191.   if (stream && LOCAL && LOCAL->nntpstream) {
  192.                 /* begin with a host specification? */
  193.     if (((*pat == '{') || ((*pat == '*') && (pat[1] == '{'))) &&
  194.     (t = strchr (pat,'}')) && *(patx = ++t)) {
  195.       if (*pat == '*') pat++;    /* yes, skip leading * (old Pine behavior) */
  196.       strcpy (tmp,pat);        /* copy host name */
  197.       bbd = tmp + (patx - pat);    /* where we write the bboards */
  198.     }
  199.     else {            /* no host specification */
  200.       bbd = tmp;        /* no prefix */
  201.       patx = pat;        /* use entire specification */
  202.     }
  203.                 /* ask server for all active newsgroups */
  204.     if (!(smtp_send (LOCAL->nntpstream,"LIST","ACTIVE") == NNTPGLIST)) return;
  205.                 /* process data until we see final dot */
  206.     while ((s = tcp_getline (LOCAL->nntpstream->tcpstream)) && *s != '.') {
  207.                 /* tie off after newsgroup name */
  208.       if (t = strchr (s,' ')) *t = '\0';
  209.       if (pmatch (s,patx)) {    /* report to main program if have match */
  210.     strcpy (bbd,s);        /* write newsgroup name after prefix */
  211.     mm_bboard (tmp);
  212.       }
  213.       fs_give ((void **) &s);    /* clean up */
  214.     }
  215.   }
  216. }
  217.  
  218. /* NNTP mail subscribe to mailbox
  219.  * Accepts: mail stream
  220.  *        mailbox to add to subscription list
  221.  * Returns: T on success, NIL on failure
  222.  */
  223.  
  224. long nntp_subscribe (MAILSTREAM *stream,char *mailbox)
  225. {
  226.   return NIL;            /* never valid for NNTP */
  227. }
  228.  
  229.  
  230. /* NNTP mail unsubscribe to mailbox
  231.  * Accepts: mail stream
  232.  *        mailbox to delete from subscription list
  233.  * Returns: T on success, NIL on failure
  234.  */
  235.  
  236. long nntp_unsubscribe (MAILSTREAM *stream,char *mailbox)
  237. {
  238.   return NIL;            /* never valid for NNTP */
  239. }
  240.  
  241.  
  242. /* NNTP mail subscribe to bboard
  243.  * Accepts: mail stream
  244.  *        bboard to add to subscription list
  245.  * Returns: T on success, NIL on failure
  246.  */
  247.  
  248. long nntp_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  249. {
  250.   char *s = strchr (mailbox,'}');
  251.   return s ? nntp_update_sdb (s+1,":") : NIL;
  252. }
  253.  
  254.  
  255. /* NNTP mail unsubscribe to bboard
  256.  * Accepts: mail stream
  257.  *        bboard to delete from subscription list
  258.  * Returns: T on success, NIL on failure
  259.  */
  260.  
  261. long nntp_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  262. {
  263.   char *s = strchr (mailbox,'}');
  264.   return s ? nntp_update_sdb (s+1,"!") : NIL;
  265. }
  266.  
  267. /* NNTP mail create mailbox
  268.  * Accepts: mail stream
  269.  *        mailbox name to create
  270.  * Returns: T on success, NIL on failure
  271.  */
  272.  
  273. long nntp_create (MAILSTREAM *stream,char *mailbox)
  274. {
  275.   return NIL;            /* never valid for NNTP */
  276. }
  277.  
  278.  
  279. /* NNTP mail delete mailbox
  280.  *        mailbox name to delete
  281.  * Returns: T on success, NIL on failure
  282.  */
  283.  
  284. long nntp_delete (MAILSTREAM *stream,char *mailbox)
  285. {
  286.   return NIL;            /* never valid for NNTP */
  287. }
  288.  
  289.  
  290. /* NNTP mail rename mailbox
  291.  * Accepts: mail stream
  292.  *        old mailbox name
  293.  *        new mailbox name
  294.  * Returns: T on success, NIL on failure
  295.  */
  296.  
  297. long nntp_rename (MAILSTREAM *stream,char *old,char *new)
  298. {
  299.   return NIL;            /* never valid for NNTP */
  300. }
  301.  
  302. /* NNTP mail open
  303.  * Accepts: stream to open
  304.  * Returns: stream on success, NIL on failure
  305.  */
  306.  
  307. MAILSTREAM *nntp_mopen (MAILSTREAM *stream)
  308. {
  309.   long i,j,k;
  310.   long nmsgs = 0;
  311.   long recent = 0;
  312.   long unseen = 0;
  313.   char c = NIL,*s,*t,tmp[MAILTMPLEN];
  314.   NETMBX mb;
  315.   FILE *f = NIL;
  316.   void *tcpstream;
  317.   SMTPSTREAM *nstream = NIL;
  318.                 /* return prototype for OP_PROTOTYPE call */
  319.   if (!stream) return &nntpproto;
  320.   mail_valid_net_parse (stream->mailbox,&mb);
  321.                 /* hokey default for local host */
  322.   if (!lhostn) lhostn = cpystr ("localhost");
  323.   if (!*mb.mailbox) strcpy (mb.mailbox,"general");
  324.   if (LOCAL) {            /* if recycle stream, see if changing hosts */
  325.     if (strcmp (lcase (mb.host),lcase (strcpy (tmp,LOCAL->host)))) {
  326.       sprintf (tmp,"Closing connection to %s",LOCAL->host);
  327.       if (!stream->silent) mm_log (tmp,(long) NIL);
  328.     }
  329.     else {            /* same host, preserve NNTP connection */
  330.       sprintf (tmp,"Reusing connection to %s",LOCAL->host);
  331.       if (!stream->silent) mm_log (tmp,(long) NIL);
  332.       nstream = LOCAL->nntpstream;
  333.       LOCAL->nntpstream = NIL;/* keep nntp_close() from punting it */
  334.     }
  335.     nntp_close (stream);    /* do close action */
  336.     stream->dtb = &nntpdriver;/* reattach this driver */
  337.     mail_free_cache (stream);    /* clean up cache */
  338.   }
  339.  
  340.                 /* open NNTP now if not already open */
  341.   if (!nstream && (tcpstream = tcp_open (mb.host,mb.port ?
  342.                      (long) mb.port : NNTPTCPPORT))) {
  343.     nstream = (SMTPSTREAM *) fs_get (sizeof (SMTPSTREAM));
  344.     nstream->tcpstream = tcpstream;
  345.     nstream->debug = stream->debug;
  346.     nstream->reply = NIL;
  347.                 /* get NNTP greeting */
  348.     if (smtp_reply (nstream) == NNTPGREET)
  349.       mm_log (nstream->reply + 4,(long) NIL);
  350.     else {            /* oops */
  351.       mm_log (nstream->reply,ERROR);
  352.       smtp_close (nstream);    /* punt stream */
  353.       nstream = NIL;
  354.     }
  355.   }
  356.   if (nstream) {        /* now try to open newsgroup */
  357.     if ((!stream->halfopen) &&    /* open the newsgroup if not halfopen */
  358.     ((smtp_send (nstream,"GROUP",mb.mailbox) != NNTPGOK) ||
  359.      ((nmsgs = strtol (nstream->reply + 4,&s,10)) < 0) ||
  360.      ((i = strtol (s,&s,10)) < 0) || ((j = strtol (s,&s,10)) < 0))) {
  361.       mm_log (nstream->reply,ERROR);
  362.       smtp_close (nstream);    /* punt stream */
  363.       nstream = NIL;
  364.       return NIL;
  365.     }
  366.                 /* newsgroup open, instantiate local data */
  367.     stream->local = fs_get (sizeof (NNTPLOCAL));
  368.     LOCAL->nntpstream = nstream;
  369.     LOCAL->dirty = NIL;        /* no update to .newsrc needed yet */
  370.                 /* copy host and newsgroup name */
  371.     LOCAL->host = cpystr (mb.host);
  372.     LOCAL->name = cpystr (mb.mailbox);
  373.     stream->sequence++;        /* bump sequence number */
  374.     stream->readonly = T;    /* make sure higher level knows readonly */
  375.     if (stream->halfopen) {    /* no map or state for half-open */
  376.       LOCAL->number = NIL;
  377.       LOCAL->seen = NIL;
  378.     }
  379.  
  380.     else {            /* try to get list of valid numbers */
  381.       if (smtp_send (nstream,"LISTGROUP",mb.mailbox) == NNTPGOK) {
  382.                 /* create number map */
  383.     LOCAL->number = (unsigned long *) fs_get(nmsgs*sizeof (unsigned long));
  384.                 /* initialize c-client/NNTP map */
  385.     for (i = 0; (i<nmsgs) && (s = tcp_getline (nstream->tcpstream)); ++i) {
  386.       LOCAL->number[i] = atol (s);
  387.       fs_give ((void **) &s);
  388.     }
  389.                 /* get and flush the dot-line */
  390.     if ((s = tcp_getline (nstream->tcpstream)) && (*s == '.'))
  391.       fs_give ((void **) &s);
  392.     else {            /* lose */
  393.       mm_log ("NNTP article listing protocol failure",ERROR);
  394.       nntp_close (stream);    /* do close action */
  395.     }
  396.       }
  397.       else {            /* a vanilla NNTP server, barf */
  398.                 /* any holes in sequence? */
  399.     if (nmsgs != (k = (j - i) + 1)) {
  400.       sprintf (tmp,"[HOLES] %ld non-existant message(s)",k-nmsgs);
  401.       mm_notify (stream,tmp,(long) NIL);
  402.       nmsgs = k;        /* sure are, set new message count */
  403.     }
  404.                 /* create number map */
  405.     LOCAL->number = (unsigned long *) fs_get(nmsgs*sizeof (unsigned long));
  406.                 /* initialize c-client/NNTP map */
  407.     for (k = 0; k < nmsgs; ++k) LOCAL->number[k] = i + k;
  408.       }
  409.                 /* create state list */
  410.       LOCAL->seen = (char *) fs_get (nmsgs * sizeof (char));
  411.                   /* initialize state */
  412.       for (i = 0; i < nmsgs; ++i) LOCAL->seen[i] = NIL;
  413.  
  414.       mail_exists(stream,nmsgs);/* notify upper level about mailbox size */
  415.       i = 0;            /* nothing scanned yet */
  416.       while (t = nntp_read_sdb (&f)) {
  417.     if ((s = strpbrk (t,":!")) && (c = *s)) {
  418.       *s++ = '\0';        /* tie off newsgroup name, point to data */
  419.       if (strcmp (t,LOCAL->name)) s = NIL;
  420.       else break;        /* found it! */
  421.     }
  422.     fs_give ((void **) &t);    /* give back this entry line */
  423.       }
  424.       if (s) {            /* newsgroup found? */
  425.     if (c == '!') mm_log ("Not subscribed to that newsgroup",WARN);
  426.     while (*s && i < nmsgs){/* process until run out of messages or list */
  427.       j = strtol (s,&s,10);    /* start of possible range */
  428.                 /* other end of range */
  429.       k = (*s == '-') ? strtol (++s,&s,10) : j;
  430.                 /* skip messages before this range */
  431.       while ((LOCAL->number[i] < j) && (i < nmsgs)) {
  432.         if (!unseen) unseen = i + 1;
  433.         i++;
  434.       }
  435.       while ((LOCAL->number[i] >= j) && (LOCAL->number[i] <= k) &&
  436.          (i < nmsgs)){    /* mark messages within the range as seen */
  437.         LOCAL->seen[i++] = T;
  438.         mail_elt (stream,i)->deleted = T;
  439.       }
  440.       if (*s == ',') s++;    /* skip past comma */
  441.       else if (*s) {    /* better not be anything else then */
  442.         mm_log ("Bogus syntax in news state file",ERROR);
  443.         break;        /* give up fast!! */
  444.       }
  445.     }
  446.       }
  447.       else mm_log ("No state for newsgroup found, reading as new",WARN);
  448.       if (t) {            /* need to free up cruft? */
  449.     fs_give ((void **) &t);    /* yes, give back newsrc entry */
  450.     fclose (f);        /* close the file */
  451.       }
  452.       while (i++ < nmsgs) {    /* mark all remaining messages as new */
  453.     mail_elt (stream,i)->recent = T;
  454.     ++recent;        /* count another recent message */
  455.       }
  456.       if (unseen) {        /* report first unseen message */
  457.     sprintf (tmp,"[UNSEEN] %ld is first unseen message",unseen);
  458.     mm_notify (stream,tmp,(long) NIL);
  459.       }
  460.                 /* notify upper level about recent */
  461.       mail_recent(stream,recent);
  462.                 /* notify if empty bboard */
  463.       if (!(stream->nmsgs||stream->silent)) mm_log ("Newsgroup is empty",WARN);
  464.     }
  465.   }
  466.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  467. }
  468.  
  469. /* NNTP mail close
  470.  * Accepts: MAIL stream
  471.  */
  472.  
  473. void nntp_close (MAILSTREAM *stream)
  474. {
  475.   if (LOCAL) {            /* only if a file is open */
  476.     nntp_check (stream);    /* dump final checkpoint */
  477.     if (LOCAL->name) fs_give ((void **) &LOCAL->name);
  478.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  479.     if (LOCAL->number) fs_give ((void **) &LOCAL->number);
  480.     if (LOCAL->seen) fs_give ((void **) &LOCAL->seen);
  481.                 /* close NNTP connection */
  482.     if (LOCAL->nntpstream) smtp_close (LOCAL->nntpstream);
  483.                 /* nuke the local data */
  484.     fs_give ((void **) &stream->local);
  485.     stream->dtb = NIL;        /* log out the DTB */
  486.   }
  487. }
  488.  
  489. /* NNTP mail fetch fast information
  490.  * Accepts: MAIL stream
  491.  *        sequence
  492.  */
  493.  
  494. void nntp_fetchfast (MAILSTREAM *stream,char *sequence)
  495. {
  496.   return;            /* no-op for local mail */
  497. }
  498.  
  499.  
  500. /* NNTP mail fetch flags
  501.  * Accepts: MAIL stream
  502.  *        sequence
  503.  */
  504.  
  505. void nntp_fetchflags (MAILSTREAM *stream,char *sequence)
  506. {
  507.   return;            /* no-op for local mail */
  508. }
  509.  
  510. /* NNTP mail fetch envelope
  511.  * Accepts: MAIL stream
  512.  *        message # to fetch
  513.  *        pointer to return body
  514.  * Returns: envelope of this message, body returned in body value
  515.  *
  516.  * Fetches the "fast" information as well
  517.  */
  518.  
  519. #define MAXHDR (unsigned long) 4*MAILTMPLEN
  520.  
  521. ENVELOPE *nntp_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  522. {
  523.   char *h,tmp[MAXHDR];
  524.   LONGCACHE *lelt;
  525.   ENVELOPE **env;
  526.   STRING bs;
  527.   DAWZDATA d;
  528.   BODY **b;
  529.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  530.   long m = msgno - 1;
  531.   unsigned long hdrsize;
  532.   unsigned long textsize = 0;
  533.   if (stream->scache) {        /* short cache */
  534.     if (msgno != stream->msgno){/* flush old poop if a different message */
  535.       mail_free_envelope (&stream->env);
  536.       mail_free_body (&stream->body);
  537.     }
  538.     stream->msgno = msgno;
  539.     env = &stream->env;        /* get pointers to envelope and body */
  540.     b = &stream->body;
  541.   }
  542.   else {            /* long cache */
  543.     lelt = mail_lelt (stream,msgno);
  544.     env = &lelt->env;        /* get pointers to envelope and body */
  545.     b = &lelt->body;
  546.   }
  547.  
  548.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  549.     mail_free_envelope (env);    /* flush old envelope and body */
  550.     mail_free_body (b);
  551.     sprintf (tmp,"%ld",LOCAL->number[m]);
  552.     if (smtp_send (LOCAL->nntpstream,"HEAD",tmp) == NNTPHEAD) {
  553.       nntp_slurp (stream,&hdrsize);
  554.                 /* limit header size */
  555.       if (hdrsize > MAXHDR) hdrsize = MAXHDR;
  556.       h = (char *) fs_get (hdrsize +1);
  557.                 /* read header */
  558.       read (LOCAL->fd,h,hdrsize);
  559.       h[hdrsize] = '\0';    /* ensure tied off */
  560.       close (LOCAL->fd);    /* clean up the file descriptor */
  561.     }
  562.     else {            /* failed, mark as deleted */
  563.       LOCAL->seen[m] = T;
  564.       mail_elt (stream,msgno)->deleted = T;
  565.       h = cpystr ("");        /* no header */
  566.       hdrsize = 0;        /* empty header */
  567.     }
  568.     if (body) {            /* get message text */
  569.       sprintf (tmp,"%ld",LOCAL->number[m]);
  570.       if (smtp_send (LOCAL->nntpstream,"BODY",tmp) == NNTPBODY) {
  571.     nntp_slurp (stream,&textsize);
  572.     d.fd = LOCAL->fd;    /* set initial stringstruct */
  573.     d.pos = 0;        /* starting at beginning of file */
  574.     INIT (&bs,dawz_string,(void *) &d,textsize);
  575.       }
  576.       else {            /* failed, mark as deleted */
  577.     LOCAL->seen[m] = T;
  578.     mail_elt (stream,msgno)->deleted = T;
  579.     body = NIL;        /* failed or some reason */
  580.       }
  581.     }
  582.                 /* calculate message size */
  583.     elt->rfc822_size = hdrsize + textsize;
  584.                 /* parse envelope and body */
  585.     rfc822_parse_msg (env,body ? b : NIL,h,hdrsize,body ? &bs : NIL,
  586.               lhostn,tmp);
  587.     fs_give ((void **) &h);    /* don't need header any more */
  588.                 /* clean up the file descriptor */
  589.     if (body) close ((int) bs.data);
  590.                 /* parse date */
  591.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  592.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  593.   }
  594.   if (body) *body = *b;        /* return the body */
  595.   return *env;            /* return the envelope */
  596. }
  597.  
  598. /* NNTP mail fetch message header
  599.  * Accepts: MAIL stream
  600.  *        message # to fetch
  601.  * Returns: message header in RFC822 format
  602.  */
  603.  
  604. char *nntp_fetchheader (MAILSTREAM *stream,long msgno)
  605. {
  606.   char tmp[MAILTMPLEN];
  607.   unsigned long hdrsize;
  608.   long m = msgno - 1;
  609.                 /* set default gets routine */
  610.   if (!mailgets) mailgets = mm_gets;
  611.   if (stream->text) fs_give ((void **) &stream->text);
  612.                 /* get the header */
  613.   sprintf (tmp,"%ld",LOCAL->number[m]);
  614.   if (smtp_send (LOCAL->nntpstream,"HEAD",tmp) == NNTPHEAD) {
  615.     nntp_slurp (stream,&hdrsize);
  616.                 /* return it to user */
  617.     stream->text = (*mailgets) (nntp_read,stream,hdrsize);
  618.     close (LOCAL->fd);        /* clean up the file descriptor */
  619.   }
  620.   else {            /* failed, mark as deleted */
  621.     LOCAL->seen[m] = T;
  622.     mail_elt (stream,msgno)->deleted = T;
  623.   }
  624.   return stream->text ? stream->text : "";
  625. }
  626.  
  627.  
  628. /* NNTP mail fetch message text (body only)
  629.  * Accepts: MAIL stream
  630.  *        message # to fetch
  631.  * Returns: message text in RFC822 format
  632.  */
  633.  
  634. char *nntp_fetchtext (MAILSTREAM *stream,long msgno)
  635. {
  636.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  637.   unsigned long textsize;
  638.   long m = msgno - 1;
  639.   char tmp[MAILTMPLEN];
  640.                 /* set default gets routine */
  641.   if (!mailgets) mailgets = mm_gets;
  642.   if (stream->text) fs_give ((void **) &stream->text);
  643.                 /* get the text */
  644.   sprintf (tmp,"%ld",LOCAL->number[m]);
  645.   if (smtp_send (LOCAL->nntpstream,"BODY",tmp) == NNTPBODY) {
  646.     nntp_slurp (stream,&textsize);
  647.                 /* return it to user */
  648.     stream->text = (*mailgets) (nntp_read,stream,textsize);
  649.     close (LOCAL->fd);        /* clean up the file descriptor */
  650.   }
  651.   else {            /* failed, mark as deleted */
  652.     LOCAL->seen[m] = T;
  653.     mail_elt (stream,msgno)->deleted = T;
  654.   }
  655.   elt->seen = T;        /* mark as seen */
  656.   return stream->text ? stream->text : "";
  657. }
  658.  
  659. /* NNTP fetch message body as a structure
  660.  * Accepts: Mail stream
  661.  *        message # to fetch
  662.  *        section specifier
  663.  *        pointer to length
  664.  * Returns: pointer to section of message body
  665.  */
  666.  
  667. char *nntp_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  668. {
  669.   BODY *b;
  670.   PART *pt;
  671.   unsigned long i;
  672.   unsigned long offset = 0;
  673.   unsigned long textsize;
  674.   MESSAGECACHE *elt = mail_elt (stream,m);
  675.   char tmp[MAILTMPLEN];
  676.                 /* set default gets routine */
  677.   if (!mailgets) mailgets = mm_gets;
  678.   if (stream->text) fs_give ((void **) &stream->text);
  679.                 /* make sure have a body */
  680.   if (!(nntp_fetchstructure (stream,m,&b) && b && s && *s &&
  681.     ((i = strtol (s,&s,10)) > 0))) return NIL;
  682.   do {                /* until find desired body part */
  683.                 /* multipart content? */
  684.     if (b->type == TYPEMULTIPART) {
  685.       pt = b->contents.part;    /* yes, find desired part */
  686.       while (--i && (pt = pt->next));
  687.       if (!pt) return NIL;    /* bad specifier */
  688.                 /* note new body, check valid nesting */
  689.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  690.       offset = pt->offset;    /* get new offset */
  691.     }
  692.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  693.                 /* need to go down further? */
  694.     if (i = *s) switch (b->type) {
  695.     case TYPEMESSAGE:        /* embedded message, calculate new offset */
  696.       offset = b->contents.msg.offset;
  697.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  698.     case TYPEMULTIPART:        /* multipart, get next section */
  699.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  700.     default:            /* bogus subpart specification */
  701.       return NIL;
  702.     }
  703.   } while (i);
  704.  
  705.                 /* lose if body bogus */
  706.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  707.                 /* get the text */
  708.   sprintf (tmp,"%ld",LOCAL->number[m - 1]);
  709.   if (smtp_send (LOCAL->nntpstream,"BODY",tmp) == NNTPBODY) {
  710.     nntp_slurp (stream,&textsize);
  711.                 /* move to that place in the file */
  712.     lseek (LOCAL->fd,offset,SEEK_SET);
  713.                 /* return it to user */
  714.     stream->text = (*mailgets) (nntp_read,stream,*len = b->size.ibytes);
  715.     close (LOCAL->fd);        /* clean up the file descriptor */
  716.   }
  717.   else {            /* failed, mark as deleted */
  718.     mail_elt (stream,m)->deleted = T;
  719.     LOCAL->seen[m - 1] = T;
  720.   }
  721.   elt->seen = T;        /* mark as seen */
  722.   return stream->text ? stream->text : "";
  723. }
  724.  
  725.  
  726. /* NNTP mail read
  727.  * Accepts: MAIL stream
  728.  *        number of bytes to read
  729.  *        buffer address
  730.  * Returns: T if success, NIL otherwise
  731.  */
  732.  
  733. long nntp_read (MAILSTREAM *stream,unsigned long count,char *buffer)
  734. {
  735.   return read (LOCAL->fd,buffer,(unsigned int) count) ? T : NIL;
  736. }
  737.  
  738. /* NNTP mail slurp NNTP dot-terminated text
  739.  * Accepts: MAIL stream
  740.  *        pointer to size to return
  741.  */
  742.  
  743. void nntp_slurp (MAILSTREAM *stream,unsigned long *siz)
  744. {
  745.   char *s,*t;
  746.   int i,ok = T;
  747.   char tmp[MAILTMPLEN];
  748.   if (!((s = getenv ("TMP")) || (s = getenv ("TEMP")))) s = myhomedir ();
  749.   if ((i = strlen (s)) && ((s[i-1] == '\\') || (s[i-1]=='/')))
  750.     s[i-1] = '\0';        /* tie off trailing directory delimiter */
  751.   sprintf (tmp,"%s\\%s",s,"NEWSTEXT.TMP");
  752.   if ((LOCAL->fd = open (tmp,O_BINARY|O_RDWR|O_TRUNC|O_CREAT,S_IREAD|S_IWRITE))
  753.       < 0) {
  754.     sprintf (tmp,"Can't open scratch file on %s",s);
  755.     mm_log (tmp,ERROR);
  756.   }
  757.   *siz = 0;            /* initially no data */
  758.   while (s = tcp_getline (LOCAL->nntpstream->tcpstream)) {
  759.     if (*s == '.') {        /* possible end of text? */
  760.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  761.       else {
  762.     fs_give ((void **) &s);    /* free the line */
  763.     break;            /* end of data */
  764.       }
  765.     }
  766.     else t = s;            /* want the entire line */
  767.     if (ok && (LOCAL->fd >= 0)){/* copy it to the file */
  768.       if ((write (LOCAL->fd,t,i = strlen (t)) >= 0) &&
  769.       (write (LOCAL->fd,"\015\012",2) >= 0))
  770.     *siz += i + 2;        /* tally up size of data */
  771.       else {
  772.     sprintf (tmp,"Error writing scratch file, data trucated at %lu chars",
  773.          *siz);
  774.     mm_log (tmp,ERROR);
  775.     ok = NIL;        /* don't try any more */
  776.       }
  777.     }
  778.     fs_give ((void **) &s);    /* free the line */
  779.   }
  780.   if (LOCAL->fd >= 0) {        /* making a file? */
  781.     if (ok) {            /* not if some problem happened */
  782.       write (LOCAL->fd,"\015\012",2);
  783.       *siz += 2;        /* write final newline */
  784.     }
  785.                 /* rewind to start of file */
  786.     lseek (LOCAL->fd,(unsigned long) 0,SEEK_SET);
  787.   }
  788. }
  789.  
  790. /* NNTP mail set flag
  791.  * Accepts: MAIL stream
  792.  *        sequence
  793.  *        flag(s)
  794.  */
  795.  
  796. void nntp_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  797. {
  798.   MESSAGECACHE *elt;
  799.   long i;
  800.   short f = nntp_getflags (stream,flag);
  801.   if (!f) return;        /* no-op if no flags to modify */
  802.                 /* get sequence and loop on it */
  803.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  804.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  805.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  806.       if (f&fDELETED) {        /* deletion marks it in newsrc */
  807.     elt->deleted = T;    /* mark deleted */
  808.     if (!LOCAL->seen[i]) LOCAL->seen[i] = LOCAL->dirty = T;
  809.       }
  810.       if (f&fFLAGGED) elt->flagged = T;
  811.       if (f&fANSWERED) elt->answered = T;
  812.     }
  813. }
  814.  
  815.  
  816. /* NNTP mail clear flag
  817.  * Accepts: MAIL stream
  818.  *        sequence
  819.  *        flag(s)
  820.  */
  821.  
  822. void nntp_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  823. {
  824.   MESSAGECACHE *elt;
  825.   long i;
  826.   short f = nntp_getflags (stream,flag);
  827.   if (!f) return;        /* no-op if no flags to modify */
  828.                 /* get sequence and loop on it */
  829.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  830.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  831.                 /* clear all requested flags */
  832.       if (f&fSEEN) elt->seen = NIL;
  833.       if (f&fDELETED) {
  834.     elt->deleted = NIL;    /* undelete */
  835.     if (LOCAL->seen[i]) {    /* if marked in newsrc */
  836.       LOCAL->seen[i] = NIL;    /* unmark it now */
  837.       LOCAL->dirty = T;    /* mark stream as dirty */
  838.     }
  839.       }
  840.       if (f&fFLAGGED) elt->flagged = NIL;
  841.       if (f&fANSWERED) elt->answered = NIL;
  842.     }
  843. }
  844.  
  845. /* NNTP mail search for messages
  846.  * Accepts: MAIL stream
  847.  *        search criteria
  848.  */
  849.  
  850. void nntp_search (MAILSTREAM *stream,char *criteria)
  851. {
  852.   long i,n;
  853.   char *d,tmp[MAILTMPLEN];
  854.   search_t f;
  855.                 /* initially all searched */
  856.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  857.                 /* get first criterion */
  858.   if (criteria && (criteria = strtok (criteria," "))) {
  859.                 /* for each criterion */
  860.     for (; criteria; (criteria = strtok (NIL," "))) {
  861.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  862.       switch (*ucase (criteria)) {
  863.       case 'A':            /* possible ALL, ANSWERED */
  864.     if (!strcmp (criteria+1,"LL")) f = nntp_search_all;
  865.     else if (!strcmp (criteria+1,"NSWERED")) f = nntp_search_answered;
  866.     break;
  867.       case 'B':            /* possible BCC, BEFORE, BODY */
  868.     if (!strcmp (criteria+1,"CC"))
  869.       f = nntp_search_string (nntp_search_bcc,&d,&n);
  870.     else if (!strcmp (criteria+1,"EFORE"))
  871.       f = nntp_search_date (nntp_search_before,&n);
  872.     else if (!strcmp (criteria+1,"ODY"))
  873.       f = nntp_search_string (nntp_search_body,&d,&n);
  874.     break;
  875.       case 'C':            /* possible CC */
  876.     if (!strcmp (criteria+1,"C")) 
  877.       f = nntp_search_string (nntp_search_cc,&d,&n);
  878.     break;
  879.       case 'D':            /* possible DELETED */
  880.     if (!strcmp (criteria+1,"ELETED")) f = nntp_search_deleted;
  881.     break;
  882.       case 'F':            /* possible FLAGGED, FROM */
  883.     if (!strcmp (criteria+1,"LAGGED")) f = nntp_search_flagged;
  884.     else if (!strcmp (criteria+1,"ROM"))
  885.       f = nntp_search_string (nntp_search_from,&d,&n);
  886.     break;
  887.       case 'K':            /* possible KEYWORD */
  888.     if (!strcmp (criteria+1,"EYWORD"))
  889.       f = nntp_search_flag (nntp_search_keyword,&d);
  890.     break;
  891.       case 'N':            /* possible NEW */
  892.     if (!strcmp (criteria+1,"EW")) f = nntp_search_new;
  893.     break;
  894.  
  895.       case 'O':            /* possible OLD, ON */
  896.     if (!strcmp (criteria+1,"LD")) f = nntp_search_old;
  897.     else if (!strcmp (criteria+1,"N"))
  898.       f = nntp_search_date (nntp_search_on,&n);
  899.     break;
  900.       case 'R':            /* possible RECENT */
  901.     if (!strcmp (criteria+1,"ECENT")) f = nntp_search_recent;
  902.     break;
  903.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  904.     if (!strcmp (criteria+1,"EEN")) f = nntp_search_seen;
  905.     else if (!strcmp (criteria+1,"INCE"))
  906.       f = nntp_search_date (nntp_search_since,&n);
  907.     else if (!strcmp (criteria+1,"UBJECT"))
  908.       f = nntp_search_string (nntp_search_subject,&d,&n);
  909.     break;
  910.       case 'T':            /* possible TEXT, TO */
  911.     if (!strcmp (criteria+1,"EXT"))
  912.       f = nntp_search_string (nntp_search_text,&d,&n);
  913.     else if (!strcmp (criteria+1,"O"))
  914.       f = nntp_search_string (nntp_search_to,&d,&n);
  915.     break;
  916.       case 'U':            /* possible UN* */
  917.     if (criteria[1] == 'N') {
  918.       if (!strcmp (criteria+2,"ANSWERED")) f = nntp_search_unanswered;
  919.       else if (!strcmp (criteria+2,"DELETED")) f = nntp_search_undeleted;
  920.       else if (!strcmp (criteria+2,"FLAGGED")) f = nntp_search_unflagged;
  921.       else if (!strcmp (criteria+2,"KEYWORD"))
  922.         f = nntp_search_flag (nntp_search_unkeyword,&d);
  923.       else if (!strcmp (criteria+2,"SEEN")) f = nntp_search_unseen;
  924.     }
  925.     break;
  926.       default:            /* we will barf below */
  927.     break;
  928.       }
  929.       if (!f) {            /* if can't determine any criteria */
  930.     sprintf (tmp,"Unknown search criterion: %.80s",criteria);
  931.     mm_log (tmp,ERROR);
  932.     return;
  933.       }
  934.                 /* run the search criterion */
  935.       for (i = 1; i <= stream->nmsgs; ++i)
  936.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  937.       mail_elt (stream,i)->searched = NIL;
  938.     }
  939.                 /* report search results to main program */
  940.     for (i = 1; i <= stream->nmsgs; ++i)
  941.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  942.   }
  943. }
  944.  
  945. /* NNTP mail ping mailbox
  946.  * Accepts: MAIL stream
  947.  * Returns: T if stream alive, else NIL
  948.  */
  949.  
  950. long nntp_ping (MAILSTREAM *stream)
  951. {
  952.   /* Kludge alert: SMTPSOFTFATAL is 421 which is used in NNTP to mean ``No
  953.    * next article in this group''.  Hopefully, no NNTP server will choke on
  954.    * a bogus command. */
  955.   return (smtp_send (LOCAL->nntpstream,"PING","PONG") != SMTPSOFTFATAL);
  956. }
  957.  
  958.  
  959. /* NNTP mail check mailbox
  960.  * Accepts: MAIL stream
  961.  */
  962.  
  963. void nntp_check (MAILSTREAM *stream)
  964. {
  965.   unsigned long i,j,k;
  966.   char *s,tmp[MAILTMPLEN];
  967.   if (!LOCAL->dirty) return;    /* never do if no updates */
  968.   *(s = tmp) = '\0';        /* initialize list */
  969.   for (i = 0,j = 1,k = 0; i < stream->nmsgs; ++i) {
  970.     if (LOCAL->seen[i]) {    /* seen message? */
  971.       k = LOCAL->number[i];    /* this is the top of the current range */
  972.       if (j == 0) j = k;    /* if no range in progress, start one */
  973.     }
  974.     else if (j != 0) {        /* unread message, ending a range */
  975.                 /* calculate end of range */
  976.       if (k = LOCAL->number[i] - 1) {
  977.                 /* dump range */
  978.     sprintf (s,(j == k) ? "%ld," : "%ld-%ld,",j,k);
  979.     s += strlen (s);    /* find end of string */
  980.       }
  981.       j = 0;            /* no more range in progress */
  982.     }
  983.   }
  984.   if (j) {            /* dump trailing range */
  985.     sprintf (s,(j == k) ? "%ld" : "%ld-%ld",j,k);
  986.     s += strlen (s);        /* find end of string */
  987.   }
  988.   else if (s[-1] == ',') s--;    /* prepare to patch out any trailing comma */
  989.   *s++ = '\0';            /* tie off string */
  990.   nntp_update_sdb (LOCAL->name,tmp);
  991. }
  992.  
  993. /* NNTP mail expunge mailbox
  994.  * Accepts: MAIL stream
  995.  */
  996.  
  997. void nntp_expunge (MAILSTREAM *stream)
  998. {
  999.   if (!stream->silent)
  1000.     mm_log ("Expunge ignored on readonly mailbox",(long) NIL);
  1001. }
  1002.  
  1003.  
  1004. /* NNTP mail copy message(s)
  1005.  * Accepts: MAIL stream
  1006.  *        sequence
  1007.  *        destination mailbox
  1008.  * Returns: T if copy successful, else NIL
  1009.  */
  1010.  
  1011. long nntp_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  1012. {
  1013.   char *s,tmp[MAILTMPLEN];
  1014.   struct stat sbuf;
  1015.   MESSAGECACHE *elt;
  1016.   time_t ti;
  1017.   struct tm *t;
  1018.   int i,j;
  1019.   unsigned long textsize;
  1020.   int fd = open (dawz_file (tmp,mailbox),O_BINARY|O_WRONLY|O_APPEND|O_CREAT,
  1021.          S_IREAD|S_IWRITE);
  1022.   if (fd < 0) {            /* got file? */
  1023.     sprintf (tmp,"Unable to open copy mailbox: %s",strerror (errno));
  1024.     mm_log (tmp,ERROR);
  1025.     return NIL;
  1026.   }
  1027.   tzset ();            /* initialize timezone stuff */
  1028.   mm_critical (stream);        /* go critical */
  1029.   fstat (fd,&sbuf);        /* get current file size */
  1030.  
  1031.                 /* for each requested message */
  1032.   for (i = 1; i <= stream->nmsgs; i++)
  1033.     if ((elt = mail_elt (stream,(long) i))->sequence) {
  1034.       sprintf (tmp,"%ld",LOCAL->number[i - 1]);
  1035.       if (smtp_send (LOCAL->nntpstream,"ARTICLE",tmp) == NNTPHEAD) {
  1036.     nntp_slurp (stream,&textsize);
  1037.     mail_date (tmp,elt);    /* make a header */
  1038.     ti = time (0);        /* get time now */
  1039.     t = localtime (&ti);    /* output local time */
  1040.     sprintf (tmp,"%d-%s-%d %02d:%02d:%02d-%s,%ld;000000000000\015\012",
  1041.          t->tm_mday,months[t->tm_mon],t->tm_year+1900,
  1042.          t->tm_hour,t->tm_min,t->tm_sec,tzname[t->tm_isdst],
  1043.          textsize);
  1044.     while (textsize) {    /* write out the message, chunk at a time */
  1045.       read (LOCAL->fd,tmp,j = min (textsize,(long) MAILTMPLEN));
  1046.       if (write (fd,tmp,j) < 0) {
  1047.         sprintf (tmp,"Unable to write message: %s",strerror (errno));
  1048.         mm_log (tmp,ERROR);
  1049.         close (LOCAL->fd);
  1050.         chsize (fd,sbuf.st_size);
  1051.         close (fd);        /* punt */
  1052.         mm_nocritical (stream);
  1053.         return NIL;
  1054.       }
  1055.       textsize -= i;    /* note we wrote out this much */
  1056.     }
  1057.     close (LOCAL->fd);    /* clean up the file descriptor */
  1058.       }
  1059.       else {            /* failed, mark as deleted */
  1060.     LOCAL->seen[i - 1] = T;
  1061.     elt->deleted = T;
  1062.       }
  1063.     }
  1064.   close (fd);            /* close the file */
  1065.   mm_nocritical (stream);    /* release critical */
  1066.   return T;
  1067. }
  1068.  
  1069. /* NNTP mail move message(s)
  1070.  * Accepts: MAIL stream
  1071.  *        sequence
  1072.  *        destination mailbox
  1073.  * Returns: T if move successful, else NIL
  1074.  */
  1075.  
  1076. long nntp_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  1077. {
  1078.   long i;
  1079.   MESSAGECACHE *elt;
  1080.   if (!(mail_sequence (stream,sequence) &&
  1081.     nntp_copy (stream,sequence,mailbox))) return NIL;
  1082.                 /* delete all requested messages */
  1083.   for (i = 1; i <= stream->nmsgs; i++)
  1084.     if ((elt = mail_elt (stream,i))->sequence) {
  1085.       elt->deleted = T;        /* mark message deleted */
  1086.       LOCAL->dirty = T;        /* mark mailbox as dirty */
  1087.       LOCAL->seen[i - 1] = T;    /* and seen for .newsrc update */
  1088.     }
  1089.   return T;
  1090. }
  1091.  
  1092.  
  1093. /* NNTP mail append message from stringstruct
  1094.  * Accepts: MAIL stream
  1095.  *        destination mailbox
  1096.  *        stringstruct of messages to append
  1097.  * Returns: T if append successful, else NIL
  1098.  */
  1099.  
  1100. long nntp_append (MAILSTREAM *stream,char *mailbox,STRING *message)
  1101. {
  1102.   mm_log ("Append not valid for NNTP",ERROR);
  1103.   return NIL;
  1104. }
  1105.  
  1106.  
  1107. /* NNTP garbage collect stream
  1108.  * Accepts: Mail stream
  1109.  *        garbage collection flags
  1110.  */
  1111.  
  1112. void nntp_gc (MAILSTREAM *stream,long gcflags)
  1113. {
  1114.   /* nothing for now */
  1115. }
  1116.  
  1117. /* Internal routines */
  1118.  
  1119.  
  1120. /* Read NNTP database
  1121.  * Accepts: pointer to subscription database file handle (NIL if first time)
  1122.  * Returns: line from the file
  1123.  */
  1124.  
  1125. char *nntp_read_sdb (FILE **f)
  1126. {
  1127.   int i;
  1128.   char *s,*t,tmp[MAILTMPLEN];
  1129.   if (!*f) {            /* first time through? */
  1130.     NEWSRC (tmp);        /* yes, make name of newsrc file and open */
  1131.     if (!(*f = fopen (tmp,"r"))) return NIL;
  1132.   }
  1133.                 /* read a line from the file */
  1134.   if (fgets (tmp,MAILTMPLEN,*f)) {
  1135.     i = strlen (tmp);        /* how many characters we got */
  1136.     if (tmp[i - 1] == '\n') {    /* got a complete line? */
  1137.       tmp[i - 1] = '\0';    /* yes, tie off the line */
  1138.       s = cpystr (tmp);        /* make return string */
  1139.     }
  1140.     else {            /* ugh, have to build from fragments */
  1141.       t = nntp_read_sdb (f);    /* recuse to get remainder */
  1142.       sprintf (s = (char *) fs_get (i + strlen (t) + 1),"%s%s",tmp,t);
  1143.       fs_give ((void **) &t);    /* discard fragment */
  1144.     }
  1145.     return s;            /* return the string we made */
  1146.   }
  1147.   fclose (*f);            /* end of file, close file */
  1148.   *f = NIL;            /* make sure caller knows */
  1149.   return NIL;            /* all done */
  1150. }
  1151.  
  1152. /* Update NNTP database
  1153.  * Accepts: newsgroup name
  1154.  *        message data
  1155.  * Returns: T if success, NIL if failure
  1156.  */
  1157.  
  1158. long nntp_update_sdb (char *name,char *data)
  1159. {
  1160.   int i = strlen (name);
  1161.   char c,*s,tmp[MAILTMPLEN],new[MAILTMPLEN];
  1162.   FILE *f = NIL,*of,*nf;
  1163.   OLDNEWSRC (tmp);        /* make name of old newsrc file */
  1164.   if (!(of = fopen (tmp,"w"))) {/* open old newsrc */
  1165.     mm_log ("Can't create backup of news state",ERROR);
  1166.     return NIL;
  1167.   }
  1168.   NEWNEWSRC (new);        /* make name of new newsrc file */
  1169.   if (!(nf = fopen (new,"w"))) {/* open new newsrc */
  1170.     mm_log ("Can't create new news state",ERROR);
  1171.     fclose (of);
  1172.     return NIL;
  1173.   }
  1174.                 /* process .newsrc file */
  1175.   while (s = nntp_read_sdb (&f)) {
  1176.     fprintf (of,"%s\n",s);    /* write to backup file */
  1177.     if ((!strncmp (s,name,i)) && (((c = s[i]) == ':') || (c == '!'))) {
  1178.       if (data) switch (*data) {/* first time we saw this entry... */
  1179.       case ':':            /* subscription request */
  1180.     if (c == '!') c = ':';    /* subscribe if unsubscribed */
  1181.     else {            /* complain if already subscribed */
  1182.       sprintf (tmp,"Already subscribed to newsgroup %s",name);
  1183.       mm_log (tmp,ERROR);
  1184.     }
  1185.     data = s + i;        /* preserve old read state */
  1186.     break;
  1187.       case '!':            /* unsubscription request? */
  1188.     if (c == ':') c = '!';    /* unsubscribe if subscribed */
  1189.     else {            /* complain if not subscribed */
  1190.       sprintf (tmp,"Not subscribed to newsgroup %s",name);
  1191.       mm_log (tmp,ERROR);
  1192.     }
  1193.     data = s + i;        /* preserve old read state */
  1194.     break;
  1195.       default:            /* update read state */
  1196.     break;
  1197.       }
  1198.                 /* write the new entry */
  1199.     fprintf (nf,"%s%c %s\n",name,c,data);
  1200.     data = NIL;        /* request satisfied */
  1201.     }
  1202.     else fprintf (nf,"%s\n",s);    /* not the entry we want, write to new file */
  1203.     fs_give ((void **) &s);
  1204.   }
  1205.  
  1206.   if (data) switch (*data) {    /* if didn't find it, make new entry */
  1207.   case ':':            /* subscription request */
  1208.     fprintf (nf,"%s: \n",name);
  1209.     break;
  1210.   case '!':            /* unsubscription request? */
  1211.     sprintf (tmp,"Not subscribed to newsgroup %s",name);
  1212.     mm_log (tmp,ERROR);
  1213.     break;
  1214.   default:            /* update read state */
  1215.     fprintf (nf,"%s: %s\n",name,data);
  1216.     break;
  1217.   }
  1218.   fclose (nf);            /* close new file */
  1219.   fclose (of);            /* close backup file */
  1220.   NEWSRC (tmp);            /* make name of current file */
  1221.   unlink (tmp);            /* remove the current file */
  1222.   if (rename (new,tmp)) {    /* rename new database to current */
  1223.     mm_log ("Can't update news state",ERROR);
  1224.     return NIL;
  1225.   }
  1226.   return LONGT;            /* return success */
  1227. }     
  1228.  
  1229.  
  1230. /* Parse flag list
  1231.  * Accepts: MAIL stream
  1232.  *        flag list as a character string
  1233.  * Returns: flag command list
  1234.  */
  1235.  
  1236. short nntp_getflags (MAILSTREAM *stream,char *flag)
  1237. {
  1238.   char tmp[MAILTMPLEN];
  1239.   char *t;
  1240.   short f = 0;
  1241.   short i,j;
  1242.   if (flag && *flag) {        /* no-op if no flag string */
  1243.                 /* check if a list and make sure valid */
  1244.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  1245.       mm_log ("Bad flag list",ERROR);
  1246.       return NIL;
  1247.     }
  1248.                 /* copy the flag string w/o list construct */
  1249.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  1250.     tmp[j] = '\0';
  1251.     t = ucase (tmp);        /* uppercase only from now on */
  1252.  
  1253.     while (*t) {        /* parse the flags */
  1254.       if (*t == '\\') {        /* system flag? */
  1255.     switch (*++t) {        /* dispatch based on first character */
  1256.     case 'S':        /* possible \Seen flag */
  1257.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  1258.       t += 4;        /* skip past flag name */
  1259.       break;
  1260.     case 'D':        /* possible \Deleted flag */
  1261.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  1262.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  1263.       t += 7;        /* skip past flag name */
  1264.       break;
  1265.     case 'F':        /* possible \Flagged flag */
  1266.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  1267.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  1268.       t += 7;        /* skip past flag name */
  1269.       break;
  1270.     case 'A':        /* possible \Answered flag */
  1271.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  1272.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  1273.       t += 8;        /* skip past flag name */
  1274.       break;
  1275.     default:        /* unknown */
  1276.       i = 0;
  1277.       break;
  1278.     }
  1279.                 /* add flag to flags list */
  1280.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  1281.     else {            /* bitch about bogus flag */
  1282.       mm_log ("Unknown system flag",ERROR);
  1283.       return NIL;
  1284.     }
  1285.       }
  1286.       else {            /* no user flags yet */
  1287.     mm_log ("Unknown flag",ERROR);
  1288.     return NIL;
  1289.       }
  1290.     }
  1291.   }
  1292.   return f;
  1293. }
  1294.  
  1295. /* Search support routines
  1296.  * Accepts: MAIL stream
  1297.  *        message number
  1298.  *        pointer to additional data
  1299.  *        pointer to temporary buffer
  1300.  * Returns: T if search matches, else NIL
  1301.  */
  1302.  
  1303. char nntp_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  1304. {
  1305.   return T;            /* ALL always succeeds */
  1306. }
  1307.  
  1308.  
  1309. char nntp_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1310. {
  1311.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1312. }
  1313.  
  1314.  
  1315. char nntp_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1316. {
  1317.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1318. }
  1319.  
  1320.  
  1321. char nntp_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1322. {
  1323.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1324. }
  1325.  
  1326.  
  1327. char nntp_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1328. {
  1329.   return NIL;            /* keywords not supported yet */
  1330. }
  1331.  
  1332.  
  1333. char nntp_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1334. {
  1335.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1336.   return (elt->recent && !elt->seen) ? T : NIL;
  1337. }
  1338.  
  1339. char nntp_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1340. {
  1341.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1342. }
  1343.  
  1344.  
  1345. char nntp_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1346. {
  1347.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1348. }
  1349.  
  1350.  
  1351. char nntp_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1352. {
  1353.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1354. }
  1355.  
  1356.  
  1357. char nntp_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1358. {
  1359.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1360. }
  1361.  
  1362.  
  1363. char nntp_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1364. {
  1365.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1366. }
  1367.  
  1368.  
  1369. char nntp_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1370. {
  1371.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1372. }
  1373.  
  1374.  
  1375. char nntp_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1376. {
  1377.   return T;            /* keywords not supported yet */
  1378. }
  1379.  
  1380.  
  1381. char nntp_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1382. {
  1383.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1384. }
  1385.  
  1386. char nntp_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1387. {
  1388.   return (char) (nntp_msgdate (stream,msgno) < n);
  1389. }
  1390.  
  1391.  
  1392. char nntp_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1393. {
  1394.   return (char) (nntp_msgdate (stream,msgno) == n);
  1395. }
  1396.  
  1397.  
  1398. char nntp_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1399. {
  1400.                 /* everybody interprets "since" as .GE. */
  1401.   return (char) (nntp_msgdate (stream,msgno) >= n);
  1402. }
  1403.  
  1404.  
  1405. unsigned long nntp_msgdate (MAILSTREAM *stream,long msgno)
  1406. {
  1407.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1408.                 /* get date if don't have it yet */
  1409.   if (!elt->day) nntp_fetchstructure (stream,msgno,NIL);
  1410.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1411. }
  1412.  
  1413.  
  1414. char nntp_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1415. {
  1416.   return NIL;            /* need code here */
  1417. }
  1418.  
  1419.  
  1420. char nntp_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1421. {
  1422.   char *t = nntp_fetchstructure (stream,msgno,NIL)->subject;
  1423.   return t ? search (t,(unsigned long) strlen (t),d,n) : NIL;
  1424. }
  1425.  
  1426.  
  1427. char nntp_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1428. {
  1429.   return NIL;            /* need code here */
  1430. }
  1431.  
  1432. char nntp_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1433. {
  1434.   char tmp[8*MAILTMPLEN];
  1435.   tmp[0] = '\0';        /* initially empty string */
  1436.                 /* get text for address */
  1437.   rfc822_write_address (tmp,nntp_fetchstructure(stream,msgno,NIL)->bcc);
  1438.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1439. }
  1440.  
  1441.  
  1442. char nntp_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1443. {
  1444.   char tmp[8*MAILTMPLEN];
  1445.   tmp[0] = '\0';        /* initially empty string */
  1446.                 /* get text for address */
  1447.   rfc822_write_address (tmp,nntp_fetchstructure (stream,msgno,NIL)->cc);
  1448.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1449. }
  1450.  
  1451.  
  1452. char nntp_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1453. {
  1454.   char tmp[8*MAILTMPLEN];
  1455.   tmp[0] = '\0';        /* initially empty string */
  1456.                 /* get text for address */
  1457.   rfc822_write_address (tmp,nntp_fetchstructure (stream,msgno,NIL)->from);
  1458.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1459. }
  1460.  
  1461.  
  1462. char nntp_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1463. {
  1464.   char tmp[8*MAILTMPLEN];
  1465.   tmp[0] = '\0';            /* initially empty string */
  1466.                 /* get text for address */
  1467.   rfc822_write_address (tmp,nntp_fetchstructure (stream,msgno,NIL)->to);
  1468.   return search (tmp,(unsigned long) strlen (tmp),d,n);
  1469. }
  1470.  
  1471. /* Search parsers */
  1472.  
  1473.  
  1474. /* Parse a date
  1475.  * Accepts: function to return
  1476.  *        pointer to date integer to return
  1477.  * Returns: function to return
  1478.  */
  1479.  
  1480. search_t nntp_search_date (search_t f,long *n)
  1481. {
  1482.   long i;
  1483.   char *s;
  1484.   MESSAGECACHE elt;
  1485.                 /* parse the date and return fn if OK */
  1486.   return (nntp_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1487.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1488. }
  1489.  
  1490. /* Parse a flag
  1491.  * Accepts: function to return
  1492.  *        pointer to string to return
  1493.  * Returns: function to return
  1494.  */
  1495.  
  1496. search_t nntp_search_flag (search_t f,char **d)
  1497. {
  1498.                 /* get a keyword, return if OK */
  1499.   return (*d = strtok (NIL," ")) ? f : NIL;
  1500. }
  1501.  
  1502.  
  1503. /* Parse a string
  1504.  * Accepts: function to return
  1505.  *        pointer to string to return
  1506.  *        pointer to string length to return
  1507.  * Returns: function to return
  1508.  */
  1509.  
  1510. search_t nntp_search_string (search_t f,char **d,long *n)
  1511. {
  1512.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1513.   if (c) {            /* better be an argument */
  1514.     switch (*c) {        /* see what the argument is */
  1515.     case '\0':            /* catch bogons */
  1516.     case ' ':
  1517.       return NIL;
  1518.     case '"':            /* quoted string */
  1519.       if (!(strchr (c+1,'"') && (*d = strtok (c,"\"")) && (*n = strlen (*d))))
  1520.     return NIL;
  1521.       break;
  1522.     case '{':            /* literal string */
  1523.       *n = strtol (c+1,&c,10);    /* get its length */
  1524.       if (*c++ != '}' || *c++ != '\015' || *c++ != '\012' ||
  1525.       *n > strlen (*d = c)) return NIL;
  1526.       c[*n] = '\255';        /* write new delimiter */
  1527.       strtok (c,"\255");    /* reset the strtok mechanism */
  1528.       break;
  1529.     default:            /* atomic string */
  1530.       *n = strlen (*d = strtok (c," "));
  1531.       break;
  1532.     }
  1533.     return f;
  1534.   }
  1535.   else return NIL;
  1536. }
  1537.